﻿using System;
using System.Globalization;
using ManoMachine.Assembler.Exceptions;
using ManoMachine.Assembler.Parsers;

namespace ManoMachine.Assembler.Mnemonics.Descriptors
{
    class PSIDescriptor : MnemonicDescriptor
    {
        private bool AllowNumbers { get; set; }
        public PSIDescriptor(string name, bool allowNumbers) : base(name)
        {
            AllowNumbers = allowNumbers;
        }

        public override bool IsValidInstruction(AssemblyLine assemblyLine)
        {
            if (string.Compare(Name, "END", true) == 0)
            {
                return assemblyLine.InstructionParts.Length == 1;
            }

            if (assemblyLine.InstructionParts.Length != 2) 
                return false;

            try
            {
                string number = assemblyLine.InstructionParts[1];
                switch (assemblyLine.InstructionParts[0].ToUpper())
                {
                    case "ORG":
                        {
                            int i = Int32.Parse(number, NumberStyles.HexNumber);
                            if (i > 4096 || i < 0)
                            {
                                throw new AssemblerException("invalid origin", assemblyLine);
                            }
                        }
                        return true;
                    case "DEC":
                        {
                            int i = Int32.Parse(number, NumberStyles.Integer);
                            if (i > short.MaxValue || i < short.MinValue )
                            {
                                throw new AssemblerException("the number is out of range", assemblyLine);
                            }
                        }
                        return true;
                    case "HEX":
                        {
                            int i = Int32.Parse(number, NumberStyles.HexNumber);
                            if (i > ushort.MaxValue || i < ushort.MinValue)
                            {
                                throw new AssemblerException("the number is out of range", assemblyLine);
                            }
                        }
                        return true;
                }
            }
            catch(AssemblerException)
            {
                throw;
            }
            catch (Exception)
            {
                throw new AssemblerException("invalid number", assemblyLine);
            }

            return true;
        }

        /// <summary>
        /// IsValidInstruction() MUST BE called before this function.
        /// </summary>
        /// <param name="assemblyLine"></param>
        /// <returns></returns>
        public int GetNumber(AssemblyLine assemblyLine)
        {
            string number = assemblyLine.InstructionParts[1];
            switch (assemblyLine.InstructionParts[0].ToUpper())
            {
                case "ORG":
                    return Int32.Parse(number, NumberStyles.HexNumber);
                case "DEC":
                    return Int32.Parse(number, NumberStyles.Integer);
                case "HEX":
                    return Int32.Parse(number, NumberStyles.HexNumber);
                default:
                    throw new ApplicationException("PSIDescriptor::GetNumber()");
            }
        }
    }
}